home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_c / cswitc / cswitch.doc next >
Text File  |  1990-08-13  |  47KB  |  1,281 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.                           C S W I T C H
  22.                         -----------------
  23.  
  24.  
  25.  
  26.  
  27.  
  28.                Multitasking Functions for DOS Programs
  29.  
  30.                     Copyright 1990 by Herb Rose
  31.                         All Rights Reserved
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.               Version 1.0 release date : Aug 10, 1990
  57.  
  58.  
  59. This file is an introduction to the Cswitch functions.  These
  60. functions provide limited multitasking capabilities for programs
  61. running under MS-DOS or PC-DOS.
  62.  
  63. The term 'limited multitasking' is used because these routines do not
  64. provide a multitasking user interface like DESQVIEW or WINDOWS.  These
  65. routines are programming tools that will enable a C programmer to
  66. write applications that consist of independent, simultaneously executing
  67. programs.
  68.  
  69. Using Cswitch, you can execute functions as independent tasks, which
  70. execute concurrently in a time-slicing environment.  You may also load
  71. and execute programs separately.  Cswitch provides you with a priority
  72. driven scheduler and dispatcher, DOS interface, and task control
  73. functions to implement true multitasking under your program's control.
  74.  
  75. Before I continue the explanation of Cswitch, please bear with me as
  76. I detail the legalities of using these routines.
  77.  
  78.  
  79. WARRANTY
  80. --------
  81.  
  82. This software is distributed as is, with no warranty of any kind,
  83. either expressed or implied.  No warranty or guarantee is made that
  84. the software is complete or error free.  All liability and risk
  85. associated with the use of this software is assumed by the user.
  86. Under no circumstances will ADEPT Software, its owners, or its
  87. agents be responsible for any damages arising from the use, or the
  88. inability to use this product.  This applies to all damages and
  89. remedies including, but not limited to, loss of or damage to property,
  90. and loss of profits.
  91.  
  92. This software has been tested in many configurations, under various
  93. conditions, and has consistently performed acceptably.  To the best
  94. of my knowledge, there are no errors or omissions in the software
  95. that will pose a problem to the user.
  96.  
  97. The user must be aware, however, that this is a programmer's tool,
  98. and by it's very nature will affect the software that controls the
  99. user's computer.  All reasonable precautions should be taken to
  100. protect your computer from the affects of an errant operating system.
  101.  
  102. In short -
  103. BACK UP YOUR DISKS, AND BE CAREFUL WHEN TESTING NEW PROGRAMS THAT
  104. USE CSWITCH!!!!!
  105.  
  106.  
  107.  
  108. Shareware Distribution
  109. ----------------------
  110.  
  111. CSWITCH is distributed as shareware.  You may freely use and distribute
  112. these routines under the following conditions :
  113.  
  114.     1. All files on this diskette must be distributed as a set, and no
  115.        modifications or additions may be distributed as part of the
  116.        set unless specifically approved in writing by Herb Rose.
  117.  
  118.     2. All copyright and legal notices must remain intact and unaltered.
  119.  
  120.     3. Copying and distribution fees may not exceed $6, and must be
  121.        clearly specified as such.
  122.  
  123.     4. You must register Cswitch to receive a copy of the source code,
  124.        or if you wish to distribute a software product that makes use
  125.        of any of the Cswitch functions.
  126.  
  127. Note that registration is not required if you simply want to use Cswitch
  128. in the privacy of your own home, or just to see what it can do.  As an
  129. incentive to registration, I include C and assembly source code when you
  130. register.  The source code is easy to read and well documented.  Make
  131. files are included for Microsoft C and Microsoft Assembler.
  132.  
  133. Registered users are informed of new releases of the software, and may
  134. download new releases (including source code) from the INFO*SHARE BBS.
  135.  
  136. If you plan to distribute any software product that makes use of Cswitch
  137. functions, then registration is mandatory.  Registration provides you
  138. with an unlimited, nonexclusive binary distribution license, with no
  139. royalties.  Businesses that make use of Cswitch must register the
  140. program.  Site licenses may be issued.  Contact ADEPT Software for more
  141. information and pricing.
  142.  
  143. Registration is for use on a single computer.  Contact ADEPT Software
  144. for site licensing, or for multiple copy discounts.
  145.  
  146. Due to practical constraints, I cannot provide telephone assistance
  147. except to registered users.  I operate a 24-hour Bulletin Board System
  148. to handle technical assistance.  Telephone support is available for
  149. registered users, but I prefer to handle non-emergency questions via
  150. the BBS.  The number is  (703) 803-8000.
  151.  
  152.  
  153.  
  154. Registration Form
  155. -----------------
  156.  
  157. Please use the following form, or a reasonable facsimile, when
  158. registering Cswitch.
  159.  
  160.  
  161.             Mail to :    ADEPT Software
  162.                          P.O. Box 2181
  163.                          Woodbridge, Va.  22193
  164.  
  165.  
  166.     CSWITCH Registration ..................... ______ @ $30\5 = ______
  167.  
  168.     Virginia Residents add 4.5% sales tax ................. = ______
  169.  
  170.     Overseas Air Delivery ...............................$5 = ______
  171.       (U.S. delivery and Overseas Surface delivery is free)
  172.  
  173.     TOTAL ................................................. = ______
  174.  
  175.         Please send check or money order, in U.S. funds.
  176.  
  177.  
  178.         Your Name :        _____________________________________
  179.  
  180.         Company Name :     _____________________________________
  181.  
  182.         Address :          _____________________________________
  183.  
  184.         City, State, Zip : _____________________________________
  185.         (or country)
  186.  
  187.  
  188.  
  189.  
  190.  
  191.  
  192. Introduction to Multitasking
  193. ----------------------------
  194.  
  195. Multitasking is not a new concept.  Computers have been programmed to
  196. execute multiple tasks concurrently for many years.  It was observed
  197. a long time ago that most programs spent the majority of their time
  198. waiting for data input or output.  Since users and most peripheral devices
  199. are notoriously slow compared to the execution speeds of modern computers,
  200. most programs simply sit there 'twiddling their thumbs' waiting for data.
  201. It was observed that the computer's computational capabilities could be
  202. used more efficiently if another program were allowed to use the processor
  203. while a program that was just waiting was put into a dormant state.
  204. Carrying this observation one step farther, we find true multitasking.
  205. Multitasking is the ability to have several programs loaded into the
  206. computers memory, and apparently executing simultaneously.  In reality,
  207. there is a controlling program, called the Kernel, or Scheduler, that
  208. causes the computer to execute some of each programs instructions in
  209. a round-robin fashion.  Programs that are waiting for something to
  210. happen, such as waiting for a keystroke, or for a disk transfer to
  211. complete, are normally placed into a 'waiting' state, and do not get
  212. placed back into execution until the event they are waiting for has
  213. actually happened.
  214.  
  215. Notice that I said programs were 'apparently' running simultaneously.
  216. It is impossible for a microcomputer, such as the PC, to execute more
  217. that one program simultaneously.  Other computers may have several micro-
  218. processors, and can actually execute more than one task simultaneously.
  219. When there is only one microprocessor, the tasks must be executed one
  220. at a time.  The term 'time-slicing' is used to describe the operation
  221. of a multi-tasking operating system, meaning that the processor will
  222. execute each task's instructions for a short time, then will execute
  223. another task's instructions for a while, eventually allowing all the
  224. tasks to get some of their work done in a round-robin fashion.  If the
  225. amount of time given to each task is very large, say one or two seconds,
  226. there could be a significant amount of time for each task to remain
  227. idle while it awaited it's turn to execute.  If the amount of time
  228. given to each task (called the 'time slice') is too short, then too
  229. much of the computer's time will be spent saving and restoring task
  230. context information, diminishing the efficiency of the system.  A
  231. good time slice setting is one that allows many different tasks to
  232. execute in a short period of time, without noticably affecting the
  233. efficiency of the system.  Cswitch uses the timer interrupt for the
  234. PC to trigger a task switch.  This occurs approximately 18 times per
  235. second, or once every 55 milliseconds.  Since a normal time slice on
  236. a real multitasking operating system is usually around 50 microseconds,
  237. our time slice is actually very large.  In fact, 55 milliseconds is
  238. way too big for a real-time operating system, which has to react to
  239. external stimulus as quickly as possible.  With 18 tasks loaded into
  240. Cswitch, each task will only get to run once per second, for about
  241. 1/18 second.  If some of the tasks are higher priority than others,
  242. the lower priority tasks may not run at all for several seconds.  This
  243. is clearly not acceptable for programs that must react to external
  244. stimulus.
  245.  
  246. The 55 millisecond time slice, however, is fine for running one or
  247. two tasks at a high priority, and allowing several other tasks to
  248. run at a lower priority.  This gives the effect of 'background'
  249. processes running.
  250.  
  251. If you find the 55 millisecond time slice inadequate, you should
  252. consider altering the timer chip's programming so that it provides
  253. a timer interrupt more often.  Your interrupt handler will have to
  254. count the number of interrupts, and activate the DOS time-of-day
  255. routine (the normal destination of interrupt vector 8) once every
  256. 55 milliseconds, otherwise the computer's internal date and time
  257. settings will not be accurate.
  258.  
  259. Now we will focus on how you get multiple tasks to cooperate so that a
  260. given job will get done.  Basically, there are only 2 areas that we are
  261. concerned with -
  262.  
  263. 1. Passing information from one task to another, and
  264. 2. Preventing another task from interfering with a resource you are using.
  265.  
  266.  
  267. Moving Data Between Tasks
  268. -------------------------
  269.  
  270. The first area is handled in Cswitch via Message Queues.  A queue is
  271. simply a list of messages.  In this case, Cswitch has 64 queues, numbered
  272. 0-63.  Any task may place a message onto any of the queues, and any
  273. task may fetch a message from any of the queues.  If there is more than
  274. one message waiting on a queue, they will be fetched sequentially, on a
  275. first-in, first-out basis.
  276.  
  277. A message may be any format, containing any information that may be needed
  278. by another task.  When placing a message on a queue, you must tell Cswitch
  279. which queue to put it on, how long the message is (in bytes), and the
  280. address of the first byte of the message.  Cswitch will copy as many
  281. bytes as you have told it into a temporary buffer, and will place a
  282. pointer to that buffer into the message list for that queue.
  283.  
  284. When you fetch a message from a queue, you must tell Cswitch which queue
  285. to read a message from, the maximum number of bytes you are willing to
  286. receive, and where to put the message.  After Cswitch has copied the
  287. message (or as much of the message as you will allow) into your memory,
  288. the temp buffer is released, and the message pointer is deleted from
  289. the queue's list.  Note : if the message contained more bytes than
  290. you received (by specifying a maximum byte count lower than the actual
  291. size of the message), the message will still be removed from the
  292. queue.  You should make certain you allow enough room to receive any
  293. message your task is likely to receive.
  294.  
  295. It is up to the individual tasks to coordinate which queue numbers will
  296. be used to pass messages back and forth.  In the Falken BBS, a multi-
  297. line Bulletin Board System program written with Cswitch, the main
  298. BBS program assigns input and output queues to each task when it is
  299. started.  Queue number 1 is reserved, and is used to send the queue
  300. assignments to the tasks.  In other words, when a task first runs
  301. under control of Falken, it must read a message from queue number 1.
  302. That message will tell it which queue it must read messages from, and
  303. which queue it can write messages to, for the rest of the time it is
  304. running.
  305.  
  306.  
  307. Semaphores
  308. ----------
  309.  
  310. The second area of concern is to prevent another task from interfering
  311. with a resource that you are using.  A resource may be almost anything,
  312. from a disk file to a memory buffer, to a peripheral device.  Cswitch
  313. uses a semaphore system to control access to these resources.
  314.  
  315. You must know in advance which resources must be controlled with
  316. semaphores.  If you are going to allow several tasks to access a
  317. memory area simultaneously, it may be a good idea to control it, since
  318. one task may try to write new information to the memory while another
  319. task is trying to read the old information.
  320.  
  321. Semaphores work like this -
  322. A semaphore has an owner and a waiting list.  If your task attempts to
  323. 'attach' to the semaphore, but another task already 'owns' the semaphore,
  324. your task gets placed onto the 'waiting list', and does not get to run
  325. anymore (it is taken out of the ready queue).  Your task will remain
  326. dormant until it is the 'owner' of the semaphore.
  327.  
  328. When the owner of the semaphore 'releases' it, the first task in the
  329. 'waiting list' is made the new owner, and is allowed to run again.
  330.  
  331. Note : When you 'attach' to a semaphore, you will become the owner of
  332. the semaphore before you are allowed to continue.  Your
  333. program remains dormant until it is the owner, and you must release the
  334. semaphore when you are done with it, to allow other programs to run,
  335. which may be waiting for the semaphore.
  336.  
  337. If you attempt to 'attach' to a semaphore, and it is unowned, your
  338. task becomes the owner and is allowed to continue running immediately.
  339. You must still release the semaphore when you are done.
  340.  
  341.  
  342. Cswitch Functions
  343. -----------------
  344.  
  345. Cswitch allows tasks to perform the following functions :
  346.  
  347.     - All normal DOS and BIOS functions
  348.     - Send information between tasks with Message Queues
  349.     - Control system resources with Semaphores
  350.     - Alter task priority
  351.     - Suspend / Resume task execution
  352.     - Delay for a time period (sleep)
  353.     - Start new tasks, either sharing existing code, or loading a new
  354.       program file from disk.
  355.     - Load small programs into expanded memory to allow more programs
  356.       to execute simultaneously.
  357.  
  358. To use Cswitch, you must link your main program with the CSWITCH1.OBJ,
  359. CSWITCH2.OBJ and LMTC.OBJ files.  These files provide interrupt level
  360. control for task switching, and the operating system functions, such as
  361. message passing and semaphore control.  In addition, programs which
  362. will be loaded and executed under control of Cswitch must be linked
  363. with LMTC.OBJ or SMTC.OBJ (for large or small model, respectively).
  364. These files contain interface routines for calling system services.
  365.  
  366. Your main program must call the function START_MT().
  367.  
  368. START_MT() initializes the Cswitch data areas and enables multitasking.
  369. It intercepts several interrupt vectors, and builds Task Control
  370. Blocks for your main program and for an 'idle task'.  The idle task
  371. runs at a low priority, and checks the delay and termination queues
  372. regularly to service tasks waiting for those functions.
  373. After the call to START_MT(), multitasking is fully operational,
  374. and you may spawn tasks and load programs.  Note that your main
  375. program has a Task Control Block (TCB) and gets scheduled for time-
  376. slicing just as every other task does. Your main program runs at a
  377. priority of 1 (the highest priority allowed).
  378.  
  379. Before terminating, your main program MUST call the function END_MT().
  380. END_MT restores the interrupt vectors, releases all unnecessary
  381. memory, and halts all multitasking.  Essentially, it restores the
  382. computer to the same state it was in prior to the START_MT() call.
  383.  
  384. ** WARNING ** WARNING ** WARNING ** WARNING ** WARNING ** WARNING **
  385.  
  386. If you allow your program to terminate without calling END_MT, almost
  387. anything can happen, since the interrupt vectors are still pointing
  388. to Cswitch code which may or may not be overwritten by DOS.  At a
  389. minimum, your system will lock up.  A worst-case scenario could
  390. involve unrecoverable damage to disk files.
  391.  
  392. It is highly recommended that you test all your programs that use
  393. multitasking on a 'test' computer, a separate machine that does not
  394. contain any critical files or programs, to prevent loss of valuable
  395. data or programs.  At a minimum, make sure all your important files
  396. are backed up before testing new programs that use Cswitch routines.
  397.   
  398.  
  399. Executing Tasks
  400. ---------------
  401.  
  402. There are 2 methods of starting new tasks under Cswitch.  The first
  403. method is to SPAWN a new task that will execute a portion of your code
  404. independently.  Normally, this facility will be used to execute a
  405. function as a separate task.  The new task will be given it's own
  406. stack and TCB, and will be scheduled for time-slicing normally.  It
  407. will begin executing code at the address you specify (normally a
  408. function name).  It may call other functions, and may manipulate
  409. global data normally.  Any automatic (local) variables will reside
  410. on the new tasks stack, so there will not be any interference when
  411. a function is called by 2 different tasks.  In fact, you can spawn
  412. several new tasks which execute the same function if you like.
  413.  
  414. When the function being executed returns to the caller, the task will
  415. be terminated.  Any functions that are called by the spawned task will
  416. return normally, of course.  Only when the function being spawned
  417. returns will the task terminate.
  418.  
  419. The second method of starting new tasks is to load a program from
  420. disk and execute it.  The function LOADTASK() is used to load an EXE
  421. or COM file from disk and execute it.  You must supply 3 parameters
  422. to the LOADTASK function - the command line to run the program and
  423. the priority to be assigned to the task, and a flag indicating whether
  424. the task may be loaded into expanded memory or not.
  425.  
  426. For instance, to load and run the program MYPROG.EXE with the parameter
  427. 'myfile.dat', you would normally enter a DOS command line like this :
  428.  
  429. C:>myprog myfile.dat
  430.  
  431. To load and run the program under Cswitch, you would call the LOADTASK
  432. function like this :
  433.  
  434.     loadtask("myprog myfile.dat",4,1);
  435.  
  436. This loads and runs 'myprog.exe', gives it the command line parameter
  437. 'myfile.dat', and allows it to run at priority 4, and allows it to be
  438. loaded into expanded memory, if possible.
  439.  
  440. The .EXE extension is assumed if no extension is provided.  You can
  441. load and execute .COM files by specifying the .COM extension.  You
  442. must specify the full path of the program to be executed, if it is not
  443. in the current default directory.  Cswitch will not search your PATH
  444. for the program.
  445.  
  446.  
  447. Scheduling
  448. ----------
  449.  
  450. The Cswitch scheduler maintains a list of tasks that are waiting to
  451. execute, called the 'ready queue'.  When a task switch occurs, the
  452. TCB at the head of the ready queue is fetched, and allowed to run.
  453. Normally, the TCB that was just executing is inserted back into the
  454. ready queue.  In some cases, such as waiting for semaphore, delaying,
  455. etc. the task will not be re-inserted into the ready queue.
  456.  
  457. Tasks are inserted into the ready queue according to their relative
  458. priority.  Each task is assigned a 'base priority' from 1 to 10.
  459. 1 is the highest priority, and 10 is the lowest.  Each task also has
  460. a 'current priority', which is used to insert TCBs into the ready
  461. queue. Each time a task is fetched from the ready queue, the 'current
  462. priority' of all the other TCBs on the ready queue is decremented
  463. by 1.  When a task is re-inserted into the ready list, it's current
  464. priority is set equal to it's base priority, then it is inserted
  465. into the ready queue so that it is in front of all tasks whose current
  466. priority is lower.  In this way, tasks with a higher priority will
  467. be allowed to execute more often than tasks of lower priority.  The
  468. tasks with lower priority will remain on the ready queue until their
  469. current priority increases to the point that the other tasks get
  470. inserted behind them, and they work their way to the head of the queue.
  471.  
  472.  
  473. Using Expanded Memory
  474. ---------------------
  475.  
  476. Cswitch can use expanded memory to load and run tasks that require less
  477. than 64K.  By calling START_SWAPPING(), you tell Cswitch to start using
  478. expanded memory.  Any task that is loaded after calling START_SWAPPING()
  479. is  eligible to be loaded into expanded memory.
  480.  
  481.  
  482. Cswitch System Services
  483. -----------------------
  484.  
  485. Cswitch must replace some of the DOS system services with it's own
  486. services.  In every case, the calling sequence and register setup
  487. is identical do the DOS call.  Return values are also identical
  488. to DOS return values.  In short, if your program ran OK under DOS,
  489. it should run OK under Cswitch.
  490.  
  491. Cswitch replaces the following DOS (interrupt 21) services :
  492.  
  493.     48h : allocate memory
  494.  
  495.         Cswitch controls memory allocation for all tasks.  DOS uses
  496.         a 'first fit' algorithm for allocating memory, meaning that
  497.         it allocates a chunk of memory from the first block that
  498.         is large enough to fulfill the request.  This leads to memory
  499.         fragmentation, limiting the number of tasks that can run.
  500.         Cswitch uses a 'best fit' algorithm, searching all the
  501.         available memory blocks to find a block that is exactly the
  502.         size needed, or is closest to it.  This way, memory fragmentation
  503.         is kept to a minimum.
  504.  
  505.         Note : starting with DOS 3.3, you can alter the memory
  506.         allocation strategy that DOS uses, but 'first fit' is still
  507.         the default.
  508.  
  509.     49h : release memory
  510.  
  511.         Cswitch must also handle the memory release function.  When a
  512.         block of memory is released, all memory is re-combined as much
  513.         as possible to limit memory fragmentation.
  514.  
  515.     4ah : set memory block size
  516.  
  517.         This DOS function is used to adjust the size of a memory block.
  518.         Since DOS tasks expect to be the only task running, they assume
  519.         that a memory block can be altered by simply increasing the size
  520.         of the block when they need more memory.  Since several tasks
  521.         may occupy memory blocks adjacent to the block to be modified,
  522.         this is not always possible under Cswitch.  Cswitch will attempt
  523.         to fulfill the request by looking for a free block adjacent to
  524.         the specified block, and combining them to form a larger block.
  525.         If unsuccessful, it will return an error.
  526.  
  527.         Note that programs that allocate memory dynamically with CALLOC
  528.         or MALLOC calls may run into problems with this limitation.
  529.         The problem is that the library routines for allocating memory
  530.         (MALLOC, et al) use a table of structures to control the memory
  531.         blocks they obtain from DOS.  Normally, this table will only
  532.         hold 20 entries (this is true for Microsoft C and QuickC).
  533.         Since Cswitch will return an error on some of the 'set memory
  534.         block' calls, the library routines may execute an 'allocate
  535.         memory' call every time you execute MALLOC.  After 20 calls,
  536.         the table is full, and you can't allocate any more memory!
  537.  
  538.         You can avoid problems by using the EXEMOD program to change
  539.         the minimum memory requirement of your task, so that enough
  540.         memory will be reserved when your task is loaded to handle
  541.         dynamic memory allocation demands without having to request
  542.         more memory from DOS.
  543.  
  544.         Or, you can avoid using the MALLOC/CALLOC routines wherever
  545.         possible, and instead call the DOS allocate memory call
  546.         directly.  Be careful, though, because Cswitch can only
  547.         control a total of 512 memory blocks.
  548.  
  549.         When a task in expanded memory wants to extend the memory block,
  550.         it is much easier to do, provided the limit of 64K is not exceeded.
  551.         Since each task loaded into expanded memory is allocated the
  552.         entire 64K expanded memory region to work in, it is usually
  553.         possible to extend the memory allocation to the desired size.
  554.  
  555.     4ch : terminate task
  556.  
  557.         Cswitch handles task termination internally. It will release
  558.         memory held by the task, close all open files, and release the
  559.         Task Control Block.
  560.  
  561.     1ah : set new DTA area
  562.  
  563.         Cswitch records the address of the tasks new DTA in the TCB, then
  564.         passes this request to DOS normally.  This function is normally
  565.         handled by the library routines, and is seldom called explicitly
  566.         by a programmer.
  567.  
  568.     2fh : get DTA address
  569.  
  570.         Cswitch returns the DTA address from the tasks TCB.
  571.  
  572.  
  573. Cswitch Functions
  574. -----------------
  575.  
  576. The following functions are provided in the file CSWITCH1.C, and may
  577. only be called by the main program. i.e. these functions may only be
  578. called by the program that is linked with Cswitch1 and Cswitch2.
  579.  
  580.     START_MT()
  581.  
  582.         Parameters :
  583.                 none
  584.  
  585.         Description :
  586.                 Starts the multitasking kernel, and enables all system
  587.                 functions.  This function must be called once, and only
  588.                 once, before executing any of the other functions or
  589.                 system services.
  590.  
  591.         Return Value :
  592.                 none
  593.  
  594.         Notes/Comments :
  595.  
  596.  
  597.     START_SWAPPING()
  598.  
  599.         Parameters :
  600.                 none
  601.  
  602.         Description :
  603.                 This function sets Cswitch up to swap tasks out to
  604.                 expanded memory.
  605.  
  606.         Return Value :
  607.                 The amount of expanded memory available is returned,
  608.  
  609.         Notes/Comments :
  610.                 All tasks started prior to this call are memory-resident,
  611.                 and all spawned tasks are memory resident.  This routine
  612.                 may only be called once, and there is no way to turn
  613.                 off usage of expanded memory  once it is started.
  614.  
  615.  
  616.     END_MT()
  617.  
  618.         Parameters :
  619.                 none
  620.  
  621.         Description :
  622.                 Ends multitasking.  The program that called START_MT()
  623.                 must call END_MT() before terminating.
  624.  
  625.         Return Value :
  626.                 none
  627.  
  628.         Notes/Comments :
  629.                 See the warning above concerning the dire consequences
  630.                 of terminating without restoring the interrupt vectors
  631.                 and turning off multitasking.
  632.  
  633.  
  634.  
  635.  
  636.     LOADPRG(cmd_string,pri,expmemflag)
  637.     char *cmd_string;
  638.     int pri;
  639.     int expmemflag;
  640.  
  641.         Parameters :
  642.                 cmd_string is a character pointer to a command string
  643.                 to load the program, including command line parameters
  644.                 just as you would type in to load the program under
  645.                 DOS.
  646.  
  647.                 pri is the base priority of the new task
  648.  
  649.                 if expmemflag is true, the task is eligible to be loaded
  650.                 into expanded memory.  if set to 0, the task may not be
  651.                 loaded into expanded memory, even if START_SWAPPING()
  652.                 has been called previously.
  653.  
  654.         Description :
  655.                 Loads and executes an .EXE or .COM file from disk.
  656.                 The full path to the program file must be specified,
  657.                 and command line parameters may be used.
  658.  
  659.         Return Value :
  660.                  0 = no error
  661.                 -1 = not enough memory to create new task stack
  662.                 -2 = no free task control blocks
  663.                 -3 = no free memory control blocks
  664.  
  665.         Notes/Comments :
  666.                 This function performs the same service as LOADTASK()
  667.                 below.  The difference is that this service is called
  668.                 directly as a function.  LOADTASK is invoked via the
  669.                 INT 62H.  Functionally, there is no difference, since
  670.                 LOADTASK() eventually calls LOADPRG to do the work.
  671.  
  672.  
  673. Cswitch Global Variables
  674. ------------------------
  675.  
  676. The following variables are available to the main program.  These are
  677. provided for information only.
  678.  
  679.     int dos48, dos49, dos4a, dos4c;
  680.  
  681.         These integers represent a running count of how many DOS calls
  682.         (INT 21H) have been made for these function codes.
  683.  
  684.     unsigned int base_mem_segment;
  685.  
  686.         This is the segment value of the memory segment controlled by
  687.         Cswitch.
  688.  
  689.     int idlecount;
  690.  
  691.         This is a running count of how many times the idle task has
  692.         run.  The idle task is spawned when START_MT() is called, and
  693.         handles the tasks that are delaying and terminating.
  694.  
  695.     int swap_count;
  696.  
  697.         This is a running count of how many task switches have taken
  698.         place.  When the scheduler is called, this count is incremented
  699.         even if no task switch takes place due to a task locking out
  700.         switching or some other reason.
  701.  
  702.     int extmem_pages;
  703.  
  704.         This is the total number of expanded memory pages available
  705.         for task swapping. This variable is loaded after a successful
  706.         call to START_SWAPPING().
  707.  
  708.     int exmemfree;
  709.  
  710.         This is the number of free pages of expanded memory
  711.         left in the system.  Pages are allocated as needed by memory
  712.         allocation calls, so this number may vary somewhat during
  713.         operation.
  714.  
  715.  
  716. Cswitch Internal Services
  717. -------------------------
  718.  
  719. The following functions are provided in the file LMTC.OBJ and SMTC.OBJ,
  720. and may be called by any task executing under Cswitch control.  These
  721. functions allow access to the semaphore, messaging, and task control
  722. functions of Cswitch.
  723.  
  724. These functions are actually invoked via an INT 62H call.  The parameters
  725. are loaded into registers, and the INT 62H transfers control to an
  726. assembly language routine.  The registers are then pushed onto the stack,
  727. and the C function for system calls is executed.
  728.  
  729. Most of the work done by Cswitch is done by C routines, with assembly
  730. language used only for interrupt control.
  731.  
  732. All of these functions are of type INT, although some do not have return
  733. values assigned.
  734.  
  735.  
  736.  
  737.     RELINQ()
  738.  
  739.         Parameters :
  740.                 none
  741.  
  742.         Description :
  743.                 Gives up the rest of the tasks time slice.
  744.                 This is graceful way of not wasting processor time if
  745.                 your task has nothing else to do for a short while.
  746.  
  747.         Return Value :
  748.                 none
  749.  
  750.         Notes/Comments :
  751.                 none
  752.  
  753.  
  754.  
  755.     SPAWN(func_addr,pri)
  756.     int *func_addr();
  757.     int pri;
  758.  
  759.         Parameters :
  760.                 func_addr is the address of a function or subroutine
  761.                 that is to be executed as a separate task
  762.  
  763.                 pri is the base priority for the new task (1-10)
  764.  
  765.         Description :
  766.                 Causes the specified function or subroutine to be
  767.                 executed as a separate task.  A new stack is allocated
  768.                 for the task, allowing it to have exclusive access to
  769.                 local variables.  It will still share global variables
  770.                 with the calling task.
  771.  
  772.                 STDIN, STDOUT, and STDERR are inherited from the caller.
  773.  
  774.                 The function can be spawned more than once, allowing
  775.                 multiple tasks to execute the same code thread.
  776.  
  777.         Return Value :
  778.                 >0 = the tcb number of the new task, indicating no error
  779.                 -1 = not enough memory to create new task stack
  780.                 -2 = no free task control blocks
  781.                 -3 = no free memory control blocks
  782.  
  783.         Notes/Comments :
  784.                 A task that is swappable to expanded memory (i.e. one
  785.                 that was loaded after a call to START_SWAPPING() )
  786.                 cannot spawn new tasks.
  787.  
  788.  
  789.  
  790.     LOADTASK(cmd_string,pri,expmemflag)
  791.     char *cmd_string;
  792.     int pri;
  793.     int expmemflag;
  794.  
  795.         Parameters :
  796.                 cmd_string is a character pointer to a command string
  797.                 to load the program, including command line parameters
  798.                 just as you would type in to load the program under
  799.                 DOS.
  800.  
  801.                 pri is the base priority of the new task
  802.  
  803.                 if expmemflag is non-zero, the task may be loaded into
  804.                 expanded memory, if it is enabled.  If exmemflag is
  805.                 0, the task will not be loaded into expanded memory.
  806.  
  807.         Description :
  808.                 Loads and executes an .EXE or .COM file from disk.
  809.                 The full path to the program file must be specified,
  810.                 and command line parameters may be used.
  811.  
  812.                 STDIN, STDOUT, and STDERR are inherited from the caller.
  813.  
  814.         Return Value :
  815.                  0 = task loader queue is full
  816.                  1 = no error
  817.  
  818.         Notes/Comments :
  819.                 This call actually places a load request onto a queue
  820.                 to be executed by the 'idle' task.  The status of the
  821.                 load may be checked with the TEST_LOAD() call.
  822.  
  823.                 .COM files are given exactly 64K memory.  .EXE files
  824.                 are sized according to the information in the file
  825.                 header.  The MINALLOC parameter is used to determine
  826.                 how much memory, in addition to the task size and
  827.                 data space, is needed for the program.
  828.  
  829.  
  830.  
  831.     SLEEP(seconds)
  832.     int sleep;
  833.  
  834.         Parameters :
  835.                 seconds is the number of seconds the task is to remain
  836.                 alseep.
  837.  
  838.         Description :
  839.                 puts the calling task into a sleeping state, where it
  840.                 will not execute at all, for a specified number of
  841.                 seconds.
  842.  
  843.         Return Value :
  844.                 none
  845.  
  846.         Notes/Comments :
  847.  
  848.  
  849.  
  850.  
  851.     SEND_MD_MSG(queue,message_addr,count)
  852.     int queue;
  853.     char *message_addr;
  854.     int count;
  855.  
  856.         Parameters :
  857.                 queue is the message queue to place a message on
  858.                 message_addr is a pointer to the data to be placed on queue
  859.                 count is the number of bytes to place on the queue
  860.  
  861.         Description :
  862.                 places the specified number of bytes onto a message
  863.                 queue.  queues are numbered 0-63.
  864.  
  865.         Return Value :
  866.                 -1 = bad queue number
  867.                 other wise, returns number of bytes transferred
  868.  
  869.         Notes/Comments :
  870.  
  871.  
  872.  
  873.  
  874.     TESTMSG(queue)
  875.     int queue;
  876.  
  877.         Parameters :
  878.                 queue is the queue number to test
  879.  
  880.         Description :
  881.                 tests whether there are any messages waiting on the
  882.                 specified queue.
  883.  
  884.         Return Value :
  885.                 -1 = bad queue number
  886.                  0 = nothing on queue
  887.                  else, returns the number of bytes contained in the
  888.                  first message on the queue
  889.  
  890.         Notes/Comments :
  891.  
  892.  
  893.  
  894.  
  895.     RECVMSG(queue,message_addr,count)
  896.     int queue;
  897.     char *message_addr;
  898.     int count;
  899.  
  900.         Parameters :
  901.                 queue is the message queue to place a message on
  902.                 message_addr is a pointer a data area to receive the data
  903.                 count is the maximum number of bytes to transfer
  904.  
  905.         Description :
  906.                 reads the first message from the specified queue
  907.                 into the data area pointed to by message_addr. only
  908.                 the first 'count' bytes of the message are transferred.
  909.                 if more than 'count' bytes are in the message, the
  910.                 extra is lost.
  911.  
  912.                 If there are no messages on the specified queue, the
  913.                 task is placed into a dormant state until a message is
  914.                 put on the queue.  If you do not want your task to remain
  915.                 dormant if the queue is empty, you should call TESTMSG()
  916.                 to make sure something is available before calling this
  917.                 function.
  918.  
  919.         Return Value :
  920.                 -1 = bad queue number
  921.                  0 = no message was on queue
  922.                  else, returns the number of bytes transferred
  923.  
  924.         Notes/Comments :
  925.  
  926.  
  927.  
  928.  
  929.     SEM_ATTACH(semaphore_number)
  930.     int semaphore_number;
  931.  
  932.         Parameters :
  933.                 semaphore number is the number of the semaphore to
  934.                 attach (0-63)
  935.  
  936.         Description :
  937.                 attaches to the semaphore.  see semaphore description
  938.                 elsewhere in documentation
  939.  
  940.         Return Value :
  941.                 -1 = invalid semaphore number
  942.                 all other values = success
  943.  
  944.         Notes/Comments :
  945.  
  946.  
  947.  
  948.  
  949.     SEM_RELEASE(semaphore_number)
  950.     int semaphore_number;
  951.  
  952.         Parameters :
  953.                 semaphore number is the number of the semaphore to
  954.                 be released (0-63)
  955.  
  956.         Description :
  957.                 releases the semaphore.  see semaphore description
  958.                 elsewhere in documentation
  959.  
  960.         Return Value :
  961.                 -1 = invalid semaphore number
  962.                 all other values = success
  963.  
  964.  
  965.  
  966.  
  967.  
  968.     SEM_TEST(semaphore_number)
  969.     int semaphore_number;
  970.  
  971.         Parameters :
  972.                 semaphore number is the number of the semaphore to
  973.                 test (0-63)
  974.  
  975.         Description :
  976.                 tests to see if the semaphore is already owned by
  977.                 another task. see semaphore description
  978.                 elsewhere in documentation
  979.  
  980.         Return Value :
  981.                 -1 = semaphore is unowned
  982.                 all other values = semaphore is owned
  983.  
  984.  
  985.  
  986.  
  987.  
  988.     SETPRI(new_priority)
  989.     int new_priority;
  990.  
  991.         Parameters :
  992.                 new_priority is the new base priority of the calling
  993.                 task.
  994.  
  995.         Description :
  996.                 allows a task to change its priority, affecting how
  997.                 often the task gets to execute.
  998.  
  999.         Return Value :
  1000.                 none
  1001.  
  1002.         Notes/Comments :
  1003.                 only values 1-10 are legal.  the lower the number, the
  1004.                 more often a task will run.  the main program is assigned
  1005.                 a prioroty of 1, and usually should be the only task with
  1006.                 such a low priority.
  1007.  
  1008.  
  1009.  
  1010.  
  1011.     SUSPEND()
  1012.  
  1013.         Parameters :
  1014.                 none
  1015.  
  1016.         Description :
  1017.                 suspends the task until it is awakened by a WAKEUP()
  1018.                 call.
  1019.  
  1020.         Return Value :
  1021.                 none
  1022.  
  1023.         Notes/Comments :
  1024.  
  1025.  
  1026.  
  1027.  
  1028.     WAKEUP(tcb_number)
  1029.     int tcb_number;
  1030.  
  1031.         Parameters :
  1032.                 tcb_number is the tcb identifier for the task that is
  1033.                 to be awakened.
  1034.  
  1035.         Description :
  1036.                 allows a task that was suspended with a SUSPEND() call
  1037.                 to continue processing.
  1038.  
  1039.         Return Value :
  1040.                 none
  1041.  
  1042.         Notes/Comments :
  1043.                 This is the ONLY way to wake up a SUSPENDed task.
  1044.                 This call will also wake up a task that is in a
  1045.                 SLEEP state (see SLEEP() above).  In this way, a task
  1046.                 may go into a sleep state, and another task can cause
  1047.                 it to start executing again before the sleep delay is
  1048.                 over.
  1049.  
  1050.  
  1051.  
  1052.  
  1053.     HOG()
  1054.  
  1055.         Parameters :
  1056.                 none
  1057.  
  1058.         Description :
  1059.                 prevents this task from being swapped out until a
  1060.                 corresponding NOHOG() is issued.  In effect, this
  1061.                 halts multitasking, allowing the caller to run
  1062.                 forever, if desired.
  1063.  
  1064.         Return Value :
  1065.                 none
  1066.  
  1067.         Notes/Comments :
  1068.                 Use this function when your task must not be interrupted.
  1069.                 DOS functions like I/O and other critical functions are
  1070.                 already protected, so this function should not be used
  1071.                 too often.
  1072.  
  1073.  
  1074.  
  1075.  
  1076.     NOHOG()
  1077.  
  1078.         Parameters :
  1079.                 none
  1080.  
  1081.         Description :
  1082.                 allows multitasking to continue after being halted by
  1083.                 a HOG() call.
  1084.  
  1085.         Return Value :
  1086.                 none
  1087.  
  1088.         Notes/Comments :
  1089.  
  1090.  
  1091.  
  1092.  
  1093.     SPAWN_EXIT()
  1094.  
  1095.         Parameters :
  1096.                 none
  1097.  
  1098.         Description :
  1099.                 halts execution of a SPAWNed task.
  1100.  
  1101.         Return Value :
  1102.                 none
  1103.  
  1104.         Notes/Comments :
  1105.                 this is the preferred method of halting execution of a
  1106.                 spawned task.  When the spawned task is finished, it
  1107.                 should call this function to terminate.
  1108.  
  1109.  
  1110.  
  1111.  
  1112.  
  1113.     GET_TCB_INFO(pointer_address)
  1114.     struct tcb_rec far **pointer_address;
  1115.  
  1116.         Parameters :
  1117.                 pointer_address is the address of a pointer into
  1118.                 which the address of the calling tasks TCB will be placed.
  1119.  
  1120.         Description :
  1121.                 this function is used to access your Task Control Block
  1122.                 you pass in the address of a pointer, and the address
  1123.                 of your TCB is placed into the pointer.  The pointer
  1124.                 must be a LONG pointer, as supported in the LARGE
  1125.                 memory model.
  1126.  
  1127.         Return Value :
  1128.                 the TCB identifier (an index into the TCB array) is
  1129.                 returned.
  1130.  
  1131.  
  1132.         Notes/Comments :
  1133.                 Normally, tasks do not need access to their TCB.  Since
  1134.                 TCBs contain critical task control information, great
  1135.                 care must be taken when accessing the TCB.
  1136.  
  1137.  
  1138.     GET_TCB_ADDRESS(pointer_address)
  1139.     struct tcb_rec far **pointer_address;
  1140.  
  1141.         Parameters :
  1142.                 pointer_address is the address of a pointer into
  1143.                 which the address of the TCB array will be placed.
  1144.  
  1145.         Description :
  1146.                 similar to 'GET_TCB_INFO()' above, but this call
  1147.                 returns the address of the TCB array in memory, to
  1148.                 allow your task to access any given TCB, based on
  1149.                 the task number.
  1150.  
  1151.         Return Value :
  1152.                 the TCB identifier (an index into the TCB array) of
  1153.                 the calling task returned.
  1154.  
  1155.  
  1156.         Notes/Comments :
  1157.                 Normally, tasks do not need access to their TCB.  Since
  1158.                 TCBs contain critical task control information, great
  1159.                 care must be taken when accessing the TCB.
  1160.  
  1161.  
  1162.  
  1163.  
  1164.     GET_LOAD_STATUS()
  1165.  
  1166.         Parameters :
  1167.                 none
  1168.  
  1169.         Description :
  1170.                 returns the status of the last 'LOAD_TASK()' call
  1171.                 made by your program.
  1172.  
  1173.         Return Value :
  1174.                  0 = load in progress (not done yet)
  1175.                 -1 = cannot open file
  1176.                 -2 = insufficient memory
  1177.                 -3 = no TCBs available
  1178.                 -4 = no Memory Control Blocks available
  1179.  
  1180.                 otherwise, the TCB number (index into the TCB array)
  1181.                 of the new task is returned.
  1182.  
  1183.         Notes/Comments :
  1184.  
  1185.  
  1186.  
  1187.  
  1188. Sample Programs
  1189. ---------------
  1190.  
  1191. 2 sample program are included, with full source code.
  1192. MTTEST.C will load and execute the programs TEST1.EXE, TEST2.EXE,
  1193. TEST3.EXE, TEST4.EXE and TEST5.EXE.  These 5 programs read
  1194. and write a disk file.  Each time they finish reading and writing
  1195. the file, they send a message to MTTEST.  MTTEST receives and
  1196. displays the messages.  It's not particularly impressive to
  1197. watch, but it does illustrate that several programs can perform
  1198. concurrent I/O, and that programs can easily be loaded and executed
  1199. from disk.
  1200.  
  1201. MT.C is another sample program.  It demonstrates one use of the
  1202. semaphores, and the sleep() function.
  1203.  
  1204. The source files for these programs, and a make file for Microsoft
  1205. C is included.
  1206.  
  1207.  
  1208. Writing Programs With CSWITCH
  1209. -----------------------------
  1210.  
  1211. When you write your multitasking program, there are several things
  1212. to remember :
  1213.  
  1214. 1. You must use the LARGE memory model.  Programs that are linked with
  1215.    SMTC, or that are simply loaded and run, may be small model.  But
  1216.    your main program must be LARGE model.
  1217.  
  1218. 2. You must compile your main program, and any program that will SPAWN
  1219.    tasks, with the /Gs option to turn off stack checking.  This is
  1220.    imperative, and without it, your program will not run.
  1221.  
  1222. 3. C library routines are not re-entrant.  Therefore, you cannot
  1223.    do a PRINTF() from your main program, and one from a SPAWNED task
  1224.    at the same time.  In general, if a spawned task needs to do I/O,
  1225.    do it with DOS interrupts rather than with the library calls.  If
  1226.    this is impractical or impossible, then make the spawned function
  1227.    a separate program and execute it with LOADTASK().
  1228.  
  1229.    If several different programs perform simultaneous I/O there is no
  1230.    problem.  It is only when the same PRINTF or FOPEN routines are
  1231.    called from 2 different places in the same program at the same time
  1232.    that there is a problem.
  1233.  
  1234.    This probably applies to other languages as well.
  1235.  
  1236.    Also, most library routines perform stack checking upon entry.  Since
  1237.    a SPAWNED task is assigned a stack from free memory, the stack check
  1238.    will often fail, causing SPAWNED tasks to abort with a false stack
  1239.    overflow error.  If SPAWNed tasks are going to be performing complex
  1240.    functions and making a lot of library calls, they probably should
  1241.    be made into separate programs and executed with LOADTASK().
  1242.  
  1243.  
  1244. Language Support
  1245. ----------------
  1246.  
  1247. Currently, the only language directly supported is C.  Most languages
  1248. can be made to mimic C's function calls, so there should not be a
  1249. problem with linking these routines to other languages.
  1250.  
  1251. Soon, I will be adding interface modules for Turbo Pascal and QuickBasic.
  1252.  
  1253. Identifiers are prefixed with an underscore ( _ ) by the C compiler, so
  1254. if your compiler does not automatically add the underscore, you will have
  1255. to use it explicitly in the module names, and in the global data names.
  1256.  
  1257.  
  1258. Cswitch Limitations
  1259. -------------------
  1260.  
  1261. Cswitch routines will not be appropriate in every situation requiring
  1262. multitasking.  In some cases, products like Desqview, Omniview, Windows,
  1263. OS/2, and Unix are more appropriate.  Cswitch does give a programmer
  1264. an option, however.  Writing programs that multitask in the DOS
  1265. environment is a clean solution to many problems.  In general, if your
  1266. application must somehow do several things all at once, then writing
  1267. your application as a set of cooperating tasks is a lot easier than
  1268. trying to build every function into a single task.  Let your
  1269. imagination take it from there.
  1270.  
  1271. As stated previously, Cswitch does not attempt to provide a multi-
  1272. tasking user interface.  You must prevent tasks from writing to the
  1273. monitor or reading the keyboard simultaneously.  This can easily be
  1274. done with semaphores or I/O redirection.
  1275.  
  1276. Tasks that are created using the SPAWN call inherit their owners STDIN,
  1277. STDOUT and STDERR, as do tasks loaded from disk.  Normally these handles
  1278. refer to the CON device - the keyboard and monitor.  No other file
  1279. handles are inherited.
  1280.  
  1281.